import { hasChanged, isObject } from "../shared";
import { isTracking, trackEffects, triggerEffects } from "./effect";
import { reactive } from "./reactive";

class RefImpl {
    private _value: any;
    private _rawValue: any;
    public dep
    public __v_isRef = true
    constructor(value) {
        this._rawValue = value;
        this._value = convert(value)
        this.dep = new Set()
    }
    get value() {
        // 收集依赖
        trackRefValue(this)
        return this._value;
    }
    set value(newValue: any) {
        // 判断两个对象是否一样
        /* 
            这个地方是一个细节点，如果刚开始的value是一个对象，则修改的时候，
            原先的this._value会被转化为proxy对象，我们不可以让proxy对象和对象
            进行对比，只能让原始的没被reactive修饰的对象和要修改的对象进行对比
            所以，就需要一开始定义一个变量，来接收原始的对象，方便set时进行修改
        */ 
        if(hasChanged(this._rawValue,newValue)) {
            this._rawValue = newValue
            // 一定先去修改value值，再去trigger
            this._value = convert(newValue)
            triggerEffects(this.dep)
        }
    }
}

// 转换对象
// value -> obj ? reactive : value
function convert(value) {
    return isObject(value) ? reactive(value) : value
}

// 收集依赖
function trackRefValue(ref) {
    if(isTracking()) {
        trackEffects(ref.dep)
    }
}

export function ref(value) {
    return new RefImpl(value)
}

export function isRef(ref) {
    return !!ref.__v_isRef
}

export function unRef(ref) {
    // 看看是不是 ref -> ref.value
    return isRef(ref) ? ref.value : ref
}

export function proxyRefs(objectWithRefs) {
    return new Proxy(objectWithRefs,{
        get(target,key) {
            // get -> age(ref) 那么就给它返回 .value
            // not ref -> value
            return unRef(Reflect.get(target,key))
        },
        set(target,key,value) {
            // set -> ref .value
            if(isRef(target[key]) && !isRef(value)) {
                return target[key].value = value
            } else {
                return Reflect.set(target,key,value)
            }
        }
    })
}